package wangjg.mda.gen;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import wangjg.commons.character.Random;
import wangjg.mda.model.obj.Method;
import wangjg.mda.model.obj.Model;
import wangjg.mda.model.obj.ModelDataType;
import wangjg.mda.model.obj.ModelField;
import wangjg.mda.model.obj.Reference;
import wangjg.mda.tra.CodeStringUtil;
import wangjg.mda.tra.ModelUtil;
import wangjg.mda.tra.MvcUtil;
import wangjg.mda.tra.TemplateUtil;
import wangjg.mda.tra.ViewUtil;


public class Generator {
	//inner
	protected final Map<String,Object> toolBox = new HashMap<String,Object>();
	{
		toolBox.put("primitiveMap", ModelDataType.primitiveMap);
		toolBox.put("CSU", new CodeStringUtil());
		toolBox.put("CU", new CodeStringUtil());
		toolBox.put("MU", new ModelUtil());
		toolBox.put("VU", new ViewUtil());
		toolBox.put("TU", new TemplateUtil());
		toolBox.put("MVCU", new MvcUtil());
		
		toolBox.put("OracleDataType", OracleDataType.dataTypes);
		toolBox.put("PostgresDataType", PostgresDataType.dataTypes);
	}
	
	//set get
	private String templateDir;
	private List<String> templSelection;
	private String targetDir;
	private String filterExt;
	private Preprocessor preprocessor = new DefaultPreprocessor();
	
	/**
	 * 使用Model生成
	 * @param model
	 */
	public void gen(Model model){
		List<Model> list = new ArrayList<Model>();
		list.add(model);
		this.gen(list, true);
	}
	
	/**
	 * 使用列表生成
	 * @param modelList Model列表
	 * @param separate 分别使用每一Model生成
	 */
	public void gen(List<Model> modelList, boolean separate){
		//没有选择文件，全部模版
		if(this.templSelection==null || templSelection.isEmpty()){
			File templateDirFile  = new File(this.templateDir);
			this.genRecursion(modelList, templateDirFile, separate);			
		}else{
			//选择文件
			for(String path : templSelection){
				File file  = new File(path);
				this.genRecursion(modelList, file, separate);
			}
		}
	}
	
	protected void genRecursion(List<Model> modelList, File templFile, boolean separate){
		//检查是否可读
		if(!templFile.canRead()){
			return;
		}
		//检查是否目录
		if(templFile.isDirectory()){
			for(File file:templFile.listFiles()){
				genRecursion(modelList, file, separate);
			}
			return;
		}
		//检查是否应该被过滤
		if(!checkExt(templFile)){
			return;
		}
		
		if(!separate){
			Map<String,Object> rootMap = new HashMap<String,Object>();
			rootMap.put("modelList", modelList);
			this.generateOneFile(rootMap, templFile);
		}else{
			for(int i=0;i<modelList.size();i++){
				Model model = modelList.get(i);
				List<Model> _modelList = new ArrayList<Model>();
				_modelList.add(model);
				
				Map<String,Object> rootMap = new HashMap<String,Object>();
				rootMap.put("$modelIndex", i);
				rootMap.put("modelList", _modelList);
				rootMap.put("model", model);
				this.generateOneFile(rootMap, templFile);
			}
		}
	}

	protected void generateOneFile(Map<String,Object> rootMap, File templFile){
		rootMap.putAll(toolBox);
		if(preprocessor!=null){
			preprocessor.process(rootMap, templFile);
		}
		try{
			File oneOutFile = this.getModelOutFile(templFile, rootMap);
			GenUtil.make(rootMap, this.templateDir, templFile, oneOutFile);
		}catch(Exception e){
			log(e, rootMap, templFile);
		}
	}

	private void log(Exception ex, Map<String, Object> rootMap, File templFile) {
		FileOutputStream fos = null;
		try {
			String logPath = Random.getRandomStringbByTime("tmlog_", "yyyy_MM_dd__HH_mm_ss") +".txt";
			fos = new FileOutputStream(new File(this.targetDir, logPath), true);
			PrintStream ps = new PrintStream(fos, true, "utf-8");
			final String LINE = "===================" ;
			ps.println(LINE);
			ps.println("rootMap:"+rootMap);
			ps.println("templFile:"+templFile.getPath());
			ex.printStackTrace(ps);
			ps.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				if(fos!=null){fos.close();}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}

	protected File getModelOutFile(File templFile, Map<String,Object> rootMap){
		String filePath = templFile.getPath();
		//
		filePath = templFile.getPath().substring(this.templateDir.length());
		
		if(filePath!=null&&filePath.length()>0&&filePath.indexOf('$')!=-1){
			filePath = GenUtil.evaluate(rootMap, this.templateDir, filePath);
		}
		String FS = File.separator;
		if(filePath.startsWith(FS) || this.targetDir.endsWith(FS)){
			FS = "";
		}
		String target = this.targetDir+FS+filePath;
		File listFile = new File(target);
		return listFile;
	}

	/*
	 * 检查是否应该被过滤 
	 * @param templFile
	 * @return true if allow.
	 */
	private boolean checkExt(File templFile){
		if(filterExt==null || filterExt.isEmpty() || "*".equals(filterExt)){
			return true;
		}
		String[] exts = this.getFilterExt().split(",");
		for(String ext:exts){
			if(templFile.getName().endsWith("."+ext)){
				return true;
			}
		}
		return false;
	}
	
	public void setTemplateDir(String templateDir) {
		this.templateDir = templateDir;
	}

	public String getTemplateDir() {
		return templateDir;
	}
	public void setTargetDir(String targetRoot) {
		this.targetDir = targetRoot;
	}

	public String getTargetDir() {
		return targetDir;
	}
	public void setFilterExt(String filterExt) {
		this.filterExt = filterExt;
	}

	public String getFilterExt() {
		return filterExt;
	}
	
	public void setTool(String name, Object value){
		this.toolBox.put(name, value);
	}

	public List<String> getTemplSelection() {
		return templSelection;
	}

	public void setTemplSelection(List<String> templateSelection) {
		this.templSelection = templateSelection;
	}

	@Deprecated
	public static void pretreatment(List<Model> modelList){
		for(Model model : modelList){
			if(model.getFields()!=null){
				for(ModelField field:model.getFields()){
					if(ModelDataType.STRING.equals(field.getType())){
						if(field.getLength()==null){
							field.setLength(255);
						}
					}
					if(field.getReference()==null&&field.getAttributes()!=null&&field.getAttributes().get("ref")!=null){
						String str = (String) field.getAttributes().get("ref");
						String strArr[] = str.split("\\.");
						Reference ref = new Reference();
						if(strArr.length==2){
							ref.setModelName((model.getPackage()!=null?model.getPackage():"")+'.'+strArr[0]);
						}
						if(strArr.length>2){
							ref.setModelName(strArr[0]);
						}
						ref.setFiledName(strArr[strArr.length-1]);
						field.setReference(ref);
					}
				}
			}
			if(model.getMethods()!=null){
				for(Method method : model.getMethods()){
					try{
						if(method.getAttributes()!=null){
							Object reqMap = method.getAttributes().get("RequestMapping");
							if(reqMap==null || !Map.class.isAssignableFrom(reqMap.getClass())){
								continue;	
							}
							Map map = (Map)reqMap;
							Object value = map.get("value");
							if(value!=null){
								method.getAttributes().put("ACTION_URL", value);
							}
						}
					}catch(Exception e){
						e.printStackTrace();
					}
				}
			}
		}
	}

	public Preprocessor getPreprocessor() {
		return preprocessor;
	}

	public void setPreprocessor(Preprocessor preprocessor) {
		this.preprocessor = preprocessor;
	}
}
